package com.yanqu.road.server.gameplayer.module.activity.selectlottery;

import com.yanqu.road.dao.impl.activity.selectlottery.SelectLotteryJcUserDataDaoImpl;
import com.yanqu.road.dao.impl.activity.selectlottery.SelectLotteryUserDataDaoImpl;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.selectlottery.config.SelectLotteryConfig;
import com.yanqu.road.entity.activity.selectlottery.config.SelectLotteryGoodsConfig;
import com.yanqu.road.entity.activity.selectlottery.data.SelectLotteryJcUserData;
import com.yanqu.road.entity.activity.selectlottery.data.SelectLotteryUserData;
import com.yanqu.road.entity.activity.selectlottery.enums.SelectLotteryBdTypeEnum;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.log.selectlottery.LogSelectLotteryDraw;
import com.yanqu.road.exception.BusinessException;
import com.yanqu.road.logic.helper.ActivityHelper;
import com.yanqu.road.pb.activity.SelectLotteryProto;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.activity.NormalActivityMgr;
import com.yanqu.road.server.manger.activity.monjinlottery.SelectLotteryMgr;
import com.yanqu.road.server.manger.activity.monjinlottery.pb.SelectLotteryPb;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SelectLotteryModule extends GeneralModule {

    /**
     * 玩家数据MAP,k:活动ID
     */
    private Map<Integer, SelectLotteryUserData> userDataMap = new ConcurrentHashMap<>();

    /**
     * 继承玩家数据（玩家维度）,k：活动大类，k：活动小类
     */
    private Map<Integer, Map<Integer,SelectLotteryJcUserData>> jcUserDataMap = new ConcurrentHashMap<>();

    public SelectLotteryModule(GamePlayer player) {
        super(player);
    }

    @Override
    public boolean loadData() {
        SelectLotteryUserDataDaoImpl dao = new SelectLotteryUserDataDaoImpl();
        this.userDataMap = dao.getSelectLotteryUserDataMap(this.getUserId());
        SelectLotteryJcUserDataDaoImpl jcDao = new SelectLotteryJcUserDataDaoImpl();
        this.jcUserDataMap = jcDao.getSelectLotteryJcUserData(this.getUserId());
        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        //活动维度数据
        SelectLotteryUserDataDaoImpl dao = new SelectLotteryUserDataDaoImpl();
        for (SelectLotteryUserData userData : this.userDataMap.values()) {
            if (userData.isInsertOption()) {
                dao.add(userData);
            } else if (userData.isUpdateOption()){
                dao.update(userData);
            }
        }
        //继承玩家数据
        SelectLotteryJcUserDataDaoImpl jcDao = new SelectLotteryJcUserDataDaoImpl();
        for (Map<Integer,SelectLotteryJcUserData> smallTypeMap : this.jcUserDataMap.values()) {
            for (SelectLotteryJcUserData jcUserData : smallTypeMap.values()) {
                if (jcUserData.isInsertOption()) {
                    jcDao.add(jcUserData);
                } else if (jcUserData.isUpdateOption()){
                    jcDao.update(jcUserData);
                }
            }
        }
        return true;
    }

    @Override
    public void afterLogin() {
        //同步配置
        this.syncConfig();
    }

    /**
     * 同步配置
     */
    public void syncConfig() {
        //同步配置
        Map<Integer, SelectLotteryConfig> configMap = SelectLotteryMgr.getConfigMap();
        for (SelectLotteryConfig config : configMap.values()) {
            if (!ActivityHelper.activityInShowTime(config.getActivityInfo())) {
                continue;
            }
            SelectLotteryProto.SelectLotterySyncConfigMsg.Builder builder = SelectLotteryProto.SelectLotterySyncConfigMsg.newBuilder();
            builder.setActivityId(config.getActivityInfo().getActivityId());
            //等级配置
            for (SelectLotteryGoodsConfig goodsConfig : config.getGoodsConfigMap().values()) {
                builder.addLotteryGoodsConfig(SelectLotteryPb.buildSelectLotteryGoodsConfig(goodsConfig));
            }
            //推送
            player.sendPacket(Protocol.U_SELECT_LOTTERY_SYNC_CONFIG, builder);
        }
    }

    /**
     * 初始化玩家数据
     * @param activityId
     * @return
     */
    private synchronized SelectLotteryUserData initUserData(int activityId) {
        SelectLotteryUserData userData = this.userDataMap.get(activityId);
        if (userData == null) {
            //获取继承的抽取数据
            SelectLotteryJcUserData jcUserData = this.getJcDrawNum(activityId);
            int jcDrawNum = jcUserData == null ? 0 : jcUserData.getDrawNum();
            int jcSeRewardNum = jcUserData == null ? 0 : jcUserData.getSeRewardNum();
            userData = new SelectLotteryUserData(
                    activityId,
                    this.getUserId(),
                    jcDrawNum,
                    jcSeRewardNum
            );
            this.userDataMap.put(activityId, userData);
        }
        return userData;
    }

    /**
     * 获取玩家数据
     * @param activityId
     * @return
     */
    public SelectLotteryUserData getUserData(int activityId) {
        return this.userDataMap.get(activityId);
    }

    /**
     * 通用检测
     * @param activityId
     * @throws BusinessException
     */
    private void commonCheck(int activityId) throws BusinessException {
        //配置是否存在
        SelectLotteryConfig config = SelectLotteryMgr.getConfig(activityId);
        if (config == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_CONFIG_NOT_FOUND);
        }
        //活动是否进行中
        ActivityInfo activityInfo = NormalActivityMgr.getActivityInfo(activityId);
        if (activityInfo == null) {
            throw BusinessException.newException(GameErrorCode.E_ACTIVITY_NO_FOUND);
        }
        if (!ActivityHelper.activityInShowTime(activityInfo)) {
            throw BusinessException.newException(GameErrorCode.E_ACTIVITY_OVER_TIME);
        }
    }

    /**
     * 抽取一次
     * @param userData
     * @return
     */
    private synchronized SelectLotteryGoodsConfig drawOne(SelectLotteryUserData userData) throws BusinessException {
        //配置
        SelectLotteryConfig config = SelectLotteryMgr.getConfig(userData.getActivityId());
        if (config == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_CONFIG_NOT_FOUND);
        }
        //自选大奖
        SelectLotteryGoodsConfig selectReward = config.getGoodsConfig(userData.getSelectRewardId());
        //判断自选奖励是否存在
        if (selectReward == null || selectReward.getSelectNum() <= 0) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_SELECT_REWARD_NOT_EXIST);
        }
        //消耗道具
        if (config.SELECT_LOTTERY_EXTRACT_COST == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_COST_CONFIG_NOT_FOUND);
        }
        //消耗道具
        boolean costFlag = player.getModule(CurrencyModule.class).removeCurrency(config.SELECT_LOTTERY_EXTRACT_COST, eLogMoneyType.SelectLottery, eLogMoneyType.SelectLotteryDrawCost);
        if (!costFlag) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_COST_NOT_ENOUGH);
        }
        SelectLotteryGoodsConfig result;
        //判断是否命中大保底奖励
        boolean isHitSelectReward = config.isHitSelectReward(userData.getDrawNum() + 1);
        //判断是否命中保底奖励
        SelectLotteryBdTypeEnum bzType = config.isHitGuarantee(userData.getDrawNum() + 1, userData.getSeRewardNum());

        //奖励处理
        if (bzType.getType() == SelectLotteryBdTypeEnum.BIG.getType() || isHitSelectReward) {//中大保底奖
            //添加自选奖励次数
            this.addSelectNum(userData);
            //重置抽奖次数
            userData.setDrawNum(0);
            //重置2级奖励次数
            userData.setSeRewardNum(0);
            //重置自选奖励
            userData.setSelectRewardId(0);
            result = selectReward;
        } else if (bzType.getType() == SelectLotteryBdTypeEnum.SMALL.getType()) {//中小保底奖
            //权重获取小保底奖励ID
            int secondRewardId = config.getSecondRewardId();
            //重置抽奖次数
            userData.setDrawNum(0);
            //中小保底次数+1
            userData.addSeRewardNum(1);
            result = config.getGoodsConfig(secondRewardId);
        } else {//中普通奖
            //随机普通奖励
            int normalRewardId = config.randomNormalReward();
            //记录抽奖次数
            userData.addDrawNum(1);
            //是否命中小保底奖励
            if (config.ROLL_GUARANTEE_PARAM.containsKey(normalRewardId) && userData.getSeRewardNum() < config.ROLL_BIG_GUARANTEE_TIMES) {
                //重置抽奖次数
                userData.setDrawNum(0);
                //中小保底次数+1
                userData.addSeRewardNum(1);
            }
            result = config.getGoodsConfig(normalRewardId);
        }

        //添加奖励
        Property reward = PropertyHelper.parseStringToProperty(result.getRewards());
        player.getModule(CurrencyModule.class).addCurrency(reward, eLogMoneyType.SelectLottery, eLogMoneyType.SelectLotteryDrawReward);
        //是否上公告
        if (result.getIsNotice() > 0) {
            //上跨服，分发公告
            SelectLotteryProto.SelectLotteryNoticeData.Builder reqMsg = SelectLotteryProto.SelectLotteryNoticeData.newBuilder();
            reqMsg.setActivityId(userData.getActivityId());
            reqMsg.setServerId((int)GameServer.getInstance().getServerId());
            reqMsg.setUserId(player.getUserId());
            reqMsg.setRewardId(result.getId());
            reqMsg.setNickname(player.getUserInfo().getNickName());
            reqMsg.setTime(System.currentTimeMillis());
            player.sendPacket(Protocol.C2_SELECT_LOTTERY_ADD_NOTICE, reqMsg);
        }
        //日志
        LogSelectLotteryDraw log = new LogSelectLotteryDraw(
                userData.getActivityId(),
                userData.getUserId(),
                PropertyHelper.parsePropertyToString(config.SELECT_LOTTERY_EXTRACT_COST),
                result.getRewards(),
                isHitSelectReward ? 1 : 0
        );
        AutoLogMgr.add(log);
        return result;
    }

    /**
     * 添加自选奖励次数
     * @param userData
     */
    private synchronized void addSelectNum(SelectLotteryUserData userData) {
        userData.addSelectNum(userData.getSelectRewardId(), 1);
    }

    /**
     * 获取继承的玩家数据
     * @param activityType
     * @param activitySmallType
     * @return
     */
    public SelectLotteryJcUserData getJcUserData(int activityType, int activitySmallType) {
        Map<Integer,SelectLotteryJcUserData> smallTypeMap = this.jcUserDataMap.get(activityType);
        if (smallTypeMap != null) {
            return smallTypeMap.get(activitySmallType);
        }
        return null;
    }

    /**
     * 初始化继承数据
     * @param activityType
     * @param activitySmallType
     * @return
     */
    private synchronized SelectLotteryJcUserData initJcUserData(int activityType, int activitySmallType) {
        SelectLotteryJcUserData jcUserData = this.getJcUserData(activityType, activitySmallType);
        if (jcUserData == null) {
            jcUserData = new SelectLotteryJcUserData(
                    this.getUserId(),
                    activityType,
                    activitySmallType
            );
            Map<Integer,SelectLotteryJcUserData> smallTypeMap = this.jcUserDataMap.get(activityType);
            if (smallTypeMap == null) {
                smallTypeMap = new ConcurrentHashMap<>();
                this.jcUserDataMap.put(activityType, smallTypeMap);
            }
            smallTypeMap.put(activitySmallType, jcUserData);
        }
        return jcUserData;
    }

    /**
     * 更新继承的抽取数据
     * @param userData
     */
    public synchronized void updateJcDrawNum(SelectLotteryUserData userData) {
        //获取关联的主活动
        List<ActivityInfo> relationActivityList = SelectLotteryMgr.getRelationActivity(userData.getActivityId());
        if (relationActivityList.isEmpty()) {
            return;
        }
        for (ActivityInfo relationActivity : relationActivityList) {
            SelectLotteryJcUserData jcUserData = this.getJcUserData(relationActivity.getType(), relationActivity.getChildType());
            if (jcUserData == null) {
                jcUserData = this.initJcUserData(relationActivity.getType(), relationActivity.getChildType());
            }
            jcUserData.setDrawNum(userData.getDrawNum());
            jcUserData.setSeRewardNum(userData.getSeRewardNum());
        }
    }

    /**
     * 获取继承的抽取次数
     * @param activityId
     * @return
     */
    private SelectLotteryJcUserData getJcDrawNum(int activityId) {
        if (this.jcUserDataMap.isEmpty()) {
            return null;
        }
        List<ActivityInfo> relationActivityList = SelectLotteryMgr.getRelationActivity(activityId);
        for (ActivityInfo relationActivity : relationActivityList) {
            SelectLotteryJcUserData jcUserData = this.getJcUserData(relationActivity.getType(), relationActivity.getChildType());
            if (jcUserData != null) {
                //返回继承数据（如果有多个关联主活动有基础数据，默认返回第一个，这种情况是数值配置有问题）
                return jcUserData;
            }
        }
        return null;
    }

    /*********************************************交互协议********************************************************/

    /**
     * 进入活动
     * @param activityId
     * @return
     */
    public SelectLotteryUserData enterActivity(int activityId) throws BusinessException {
        //通用检测
        this.commonCheck(activityId);
        SelectLotteryUserData userData = this.getUserData(activityId);
        if (userData == null) {
            userData = this.initUserData(activityId);
        }
        //记录一下进入活动的时间
        userData.setLastEnterTime(System.currentTimeMillis());
        return userData;
    }

    /**
     * 自选奖励
     * @param activityId
     * @param rewardId
     * @throws BusinessException
     */
    public void selectReward(int activityId, int rewardId) throws BusinessException {
        //通用检测
        this.commonCheck(activityId);
        SelectLotteryUserData userData = this.getUserData(activityId);
        if (userData == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_USER_DATA_NOT_FOUND);
        }
        //判断奖励是否存在
        SelectLotteryConfig config = SelectLotteryMgr.getConfig(activityId);
        SelectLotteryGoodsConfig rewardConfig = config.getGoodsConfig(rewardId);
        if (rewardConfig == null || rewardConfig.getSelectNum() <= 0) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_SELECT_REWARD_NOT_EXIST);
        }
        //判断自选奖励次数是否达到
        int selectNum = userData.getSelectNum(rewardId);
        if (selectNum >= rewardConfig.getSelectNum()) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_SELECT_NUM_LIMIT);
        }
        //添加自选
        userData.setSelectRewardId(rewardId);
    }

    /**
     * 抽奖
     * @param activityId
     * @param num
     * @return
     * @throws BusinessException
     */
    public List<Integer> draw(int activityId, int num) throws BusinessException {
        SelectLotteryUserData userData = this.getUserData(activityId);
        if (userData == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_USER_DATA_NOT_FOUND);
        }
        //通用检测
        this.commonCheck(activityId);
        //配置
        SelectLotteryConfig config = SelectLotteryMgr.getConfig(activityId);
        //判断是否添加自选
        if (userData.getSelectRewardId() <= 0) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_NOT_ADD_SELECT_PROP);
        }
        //判断自选奖励是否正确
        SelectLotteryGoodsConfig selectRewardConfig = config.getGoodsConfig(userData.getSelectRewardId());
        if (selectRewardConfig == null || selectRewardConfig.getSelectNum() <= 0) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_SELECT_REWARD_NOT_EXIST);
        }
        //消耗道具
        if (config.SELECT_LOTTERY_EXTRACT_COST == null) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_COST_CONFIG_NOT_FOUND);
        }
        //判断道具是否足够
        Property cost = new Property(config.SELECT_LOTTERY_EXTRACT_COST);
        cost.goodCountMultiNum(num);
        boolean costIsEnough = player.getModule(CurrencyModule.class).currencyIsEnough(cost);
        if (!costIsEnough) {
            throw BusinessException.newException(GameErrorCode.E_SELECT_LOTTERY_COST_NOT_ENOUGH);
        }
        //抽取
        List<Integer> rewardIds = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            //抽取
            SelectLotteryGoodsConfig goodsConfig;
            try {
                goodsConfig = this.drawOne(userData);
            } catch (BusinessException e) {
                log.error("通用自选抽奖-抽取单次异常：{}", e.getCode());
                break;
            }
            //奖励结果
            rewardIds.add(goodsConfig.getId());
            //抽取到大奖后，停止抽取
            if (userData.getSelectRewardId() <= 0) {
                break;
            }
        }
        //更新继承的抽取数据
        this.updateJcDrawNum(userData);
        return rewardIds;
    }

    /**
     * 获取公告列表
     * @param reqMsg
     */
    public void getNoticeList(SelectLotteryProto.SelectLotteryGetNoticeListReqMsg reqMsg) throws BusinessException{
        //通用检测
        this.commonCheck(reqMsg.getActivityId());
        player.sendPacket(Protocol.C2_SELECT_LOTTERY_NOTICE_LIST, reqMsg.toBuilder());
    }
}
